#pragma once
#include<string>
#include<fcntl.h>
#include<unistd.h>
#include"../Comm/Util.hpp"
#include"../Comm/Log.hpp"
#include<sys/wait.h>
 #include <sys/resource.h>

namespace run_ns
{
    using namespace Log_ns;
    using namespace util_ns;
    class Runner
    {
    private:
        /* data */
    public:
        Runner(/* args */);
        ~Runner();

        static void SetProcLimit(int _cpu_rlim,int _mem_rlim)//_mem_rlim的单位是kB,_cpu_rlim是s
        {
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_cur = _cpu_rlim;
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU,&cpu_rlimit);

            struct rlimit mem_rlimit;
            mem_rlimit.rlim_cur = _mem_rlim*1024;//1KB
            mem_rlimit.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS,&mem_rlimit);       
        }
        static int Run(const std::string& file_name,int cpu_rlim,int mem_rlim)
        {
            //1.创建错误文件、输出文件、输入文件
            umask(0);
            int _stderr = open(Path_Util::Stderr_Path(file_name).c_str(),O_CREAT | O_WRONLY,0644);
            int _stdout = open(Path_Util::Stdout_Path(file_name).c_str(),O_CREAT | O_WRONLY,0644);
            int _stdin = open(Path_Util::Stdin_Path(file_name).c_str(),O_CREAT | O_RDONLY,0644);

            if(_stderr < 0 || _stdin < 0 || _stdout < 0)
            {
                LOG(ERROR) << "运行时打开标准文件失败" << "\n";
                return -1;
            }

            int pid = fork();

            if(pid < 0)
            {
                LOG(ERROR) << "创建进程失败" << "\n";
                ::close(_stderr);
                ::close(_stdin);
                ::close(_stdout);
                return -2;
            }

            if(pid == 0)
            {
                //设置当前进程的CPU时间和内存占用上限
                SetProcLimit(cpu_rlim,mem_rlim);

                //重定向标准输入、错误、输出到对应的文件中
                dup2(_stderr,2);
                dup2(_stdout,1);
                dup2(_stdin,0);
                //子进程
                execl(Path_Util::Exe_Path(file_name).c_str(),Path_Util::Exe_Path(file_name).c_str(),nullptr);

                //走到这里说明execl执行失败了
                LOG(ERROR) << "可执行程序执行失败,可能是参数传错了" << "\n";
                exit(1); 
            }

            else
            {
                //父进程
                int status;
                waitpid(pid,&status,0);
                ::close(_stderr);
                ::close(_stdin);
                ::close(_stdout);
                LOG(INFO) << "运行完成,INFO: " << (status & 0x7f) << "\n";
                return (status & 0x7f);//返回信号码
            }
        }
    };
    
 
    
}